package com.miya.demo.common.util;

import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * okhttp工具类
 *
 * @author CaiXiaowei
 * @date 2022/08/30
 */
@Slf4j
public class OkHttpUtil {

    private static volatile OkHttpClient okHttpClient = null;
    private static volatile Semaphore semaphore = null;
    private Map<String, String> headerMap;
    private String url;
    private Request.Builder request;

    /**
     * 初始化okHttpClient，并且允许https访问
     */
    private OkHttpUtil() {
        if (okHttpClient == null) {
            synchronized (OkHttpUtil.class) {
                if (okHttpClient == null) {
                    okHttpClient = new OkHttpClient.Builder()
                            .connectTimeout(15, TimeUnit.SECONDS)
                            .writeTimeout(20, TimeUnit.SECONDS)
                            .readTimeout(20, TimeUnit.SECONDS)
                            .hostnameVerifier((hostName, session) -> true)
                            .retryOnConnectionFailure(true)
                            .build();
                    this.addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
                }
            }
        }
    }

    /**
     * 用于异步请求时，控制访问线程数，返回结果
     *
     * @return
     */
    private static Semaphore getSemaphoreInstance() {
        //只能1个线程同时访问
        synchronized (OkHttpUtil.class) {
            if (semaphore == null) {
                semaphore = new Semaphore(0);
            }
        }
        return semaphore;
    }

    /**
     * 创建OkHttpUtil
     *
     * @return
     */
    public static OkHttpUtil builder() {
        return new OkHttpUtil();
    }

    /**
     * 添加url
     *
     * @param url
     * @return
     */
    public OkHttpUtil url(final String url) {
        this.url = url;
        return this;
    }


    /**
     * 添加请求头
     *
     * @param key   参数名
     * @param value 参数值
     * @return
     */
    public OkHttpUtil addHeader(final String key, final String value) {
        if (this.headerMap == null) {
            this.headerMap = new LinkedHashMap<>(16);
        }
        this.headerMap.put(key, value);
        return this;
    }

    /**
     * 初始化get方法
     *
     * @param paramMap 参数集合
     * @return
     */
    public OkHttpUtil get(final Map<String, Object> paramMap) {
        this.request = new Request.Builder().get();
        final StringBuilder urlBuilder = new StringBuilder(this.url);
        if (paramMap != null) {
            urlBuilder.append("?");
            try {
                for (final Map.Entry<String, Object> entry : paramMap.entrySet()) {
                    urlBuilder.append(URLEncoder.encode(entry.getKey(), "utf-8")).
                            append("=").
                            append(URLEncoder.encode(entry.getValue() + "", "utf-8")).
                            append("&");
                }
            } catch (final Exception e) {
                log.error("初始化get方法异常：{}", e);
            }
            urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        }
        this.request.url(urlBuilder.toString());
        return this;
    }

    /**
     * 初始化post方法
     *
     * @param isJsonPost true等于json的方式提交数据，类似postman里post方法的raw
     *                   false等于普通的表单提交
     * @param paramMap   参数集合
     * @return
     */
    public OkHttpUtil post(final boolean isJsonPost, final Map<String, String> paramMap) {
        final RequestBody requestBody;
        if (isJsonPost) {
            String json = "";
            if (paramMap != null) {
                json = JSONUtil.toJsonStr(paramMap);
            }
            requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
        } else {
            final FormBody.Builder formBody = new FormBody.Builder();
            if (paramMap != null) {
                paramMap.forEach(formBody::add);
            }
            requestBody = formBody.build();
        }
        this.request = new Request.Builder().post(requestBody).url(this.url);
        return this;
    }

    /**
     * 初始化post方法（传入JSON）
     *
     * @return
     */
    public OkHttpUtil post(String json) {
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);
        this.request = new Request.Builder().post(requestBody).url(this.url);
        return this;
    }

    /**
     * 同步请求（返回json）
     *
     * @return
     */
    public String sync() {
        this.setHeader(this.request);
        try {
            final Response response = okHttpClient.newCall(this.request.build()).execute();
            assert response.body() != null;
            return response.body().string();
        } catch (final IOException e) {
            log.error("同步请求返回json异常{}", e);
            return "请求失败：" + e.getMessage();
        }
    }


    /**
     * 同步请求（返回响应对象）
     *
     * @return
     */
    public <T> T sync(final Class<T> clazz) {
        this.setHeader(this.request);
        try {
            final Response response = okHttpClient.newCall(this.request.build()).execute();
            assert response.body() != null;
            String json = response.body().string();
            log.info("response=>{}", json);
            return JSONUtil.toBean(json, clazz);
        } catch (final IOException e) {
            log.error("同步请求（返回响应对象）{}", e);
            return null;
        }
    }

    /**
     * 为request添加请求头
     *
     * @param request
     */
    private void setHeader(final Request.Builder request) {
        if (this.headerMap != null) {
            try {
                for (final Map.Entry<String, String> entry : this.headerMap.entrySet()) {
                    request.addHeader(entry.getKey(), entry.getValue());
                }
            } catch (final Exception e) {
                log.error("为request添加请求头异常{}", e);
            }
        }
    }




    /**
     * 自定义一个接口回调
     */
    public interface ICallBack<T> {
        /**
         * 成功的回调方法
         *
         * @param call
         * @param data
         */
        void onSuccessful(Call call, T data);

        /**
         * 失败的回调方法
         *
         * @param call
         * @param errorMsg
         */
        void onFailure(Call call, String errorMsg);

    }
}
